package com.kai.auth.filter;

import com.alibaba.fastjson.JSON;
import com.kai.admin.api.TokenApi;
import com.kai.admin.data.SourceUser;
import com.kai.auth.config.SecurityProperties;
import com.kai.auth.data.LoginUser;
import com.kai.auth.enums.AuthErrorCodeConstants;
import com.kai.auth.util.*;
import com.kai.common.data.Result;
import com.kai.common.util.WebUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * token 过滤器
 */
@Slf4j
@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final SecurityProperties securityProperties;

    private final Map<HttpMethod, Set<String>> permitAllUrlsFromAnnotationMap;

    private final TokenApi tokenApi;

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain chain) throws ServletException, IOException {
        // 情况一，基于 header[login-user] 获得用户，例如说来自 Gateway 或者其它服务透传
        LoginUser loginUser = buildLoginUserByHeader(request);

        // 情况二，基于 Token 获得用户
        // 注意，这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。
        if (Objects.isNull(loginUser)) {
            String accessToken = request.getHeader(securityProperties.getTokenHeader());
            if (StringUtils.isBlank(accessToken)) {
                WebUtil.writeJson(Result.fail(AuthErrorCodeConstants.TOKEN_IS_NULL), response);
                return;
            }

            log.info("Access-Token: {}", accessToken);

            try {
                // 获取登录用户
                SourceUser sourceUser = tokenApi.checkToken(accessToken).getBody();
                loginUser = LoginUserUtil.convert(sourceUser);
            } catch (Exception e) {
                WebUtil.writeJson(Result.fail(AuthErrorCodeConstants.TOKEN_ERROR), response);
                return;
            }

            if (Objects.isNull(loginUser)) {
                WebUtil.writeJson(Result.fail(AuthErrorCodeConstants.TOKEN_EXPIRATION), response);
                return;
            }
        }

        // 设置登录用户
        LoginUserUtil.setLoginUser(loginUser);
        // 继续过滤链
        chain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String method = request.getMethod();

        boolean flag = false;
        for (HttpMethod httpMethod : permitAllUrlsFromAnnotationMap.keySet()) {
            if (httpMethod.name().equalsIgnoreCase(method) && PatternUtil.matches(permitAllUrlsFromAnnotationMap.get(httpMethod), requestURI)) {
                flag = true;
            }
        }

        return PatternUtil.matches(securityProperties.getPermitAllUrls(), requestURI) || flag;
    }

    private LoginUser buildLoginUserByHeader(HttpServletRequest request) {
        String loginUserStr = request.getHeader(LoginUserUtil.LOGIN_USER_HEADER);
        return StringUtils.isNotBlank(loginUserStr) ? JSON.parseObject(loginUserStr, LoginUser.class) : null;
    }
}
